//! @Author: DengLibin
//! @Date: Create in 2023-11-02 09:41:49
//! @Description: 文件操作

use std::{
    fmt::Display,
    fs::{self, File, OpenOptions},
    io::{self, BufRead, BufReader, Read, Write},
    path::Path,
};

use encoding::{all::GBK, EncoderTrap};
use encoding::{DecoderTrap, Encoding};

pub struct FileError {
    pub msg: String,
}
impl std::fmt::Display for FileError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.msg)
    }
}

// 自定义结果
type FileResult<T> = std::result::Result<T, FileError>;

//转换结果
fn check_result<T, E: Display>(result: Result<T, E>) -> FileResult<T> {
    return match result {
        Ok(r) => Result::Ok(r),
        Err(err) => Result::Err(FileError {
            msg: err.to_string(),
        }),
    };
}

/// @Author: DengLibin
/// @Date: Create in 2024-04-14 20:05:16
/// @Description: 创建文件夹
pub fn create_dr(path: &str) -> FileResult<()> {
    check_result(fs::create_dir(path))
}

/// @Author: DengLibin
/// @Date: Create in 2024-04-14 20:05:48
/// @Description: 创建多级文件夹
pub fn create_dir_all(path: &str) -> FileResult<()> {
    let r = check_result(fs::exists(path))?;
    if r {
        return Ok(());
    }
    check_result(fs::create_dir_all(path))
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-01 16:38:19
/// @Description: 读取utf-8编码文件字符串
pub fn read_utf8_file_str(file_path: &str) -> FileResult<String> {
    let mut file: std::fs::File = check_result(std::fs::File::open(file_path))?;
    let mut contents = String::new();
    check_result(file.read_to_string(&mut contents))?;
    Ok(contents)
}
/// @Author: DengLibin
/// @Date: Create in 2023-11-01 16:38:19
/// @Description: 读取gbk编码文件字符串
pub fn read_gbk_file_str(file_path: &str) -> FileResult<String> {
    let file: std::fs::File = check_result(std::fs::File::open(file_path))?;
    let reader = BufReader::new(&file);

    let lines = reader.split(b'\n');
    let mut contents = String::new();

    for line in lines {
        let v8 = check_result(line)?;
        let decoded_string = check_result(GBK.decode(&v8, DecoderTrap::Strict))?;
        contents.push_str(&decoded_string);
    }
    Ok(contents)
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 10:07:46
/// @Description: gbk编码文件转utf8文件
pub fn gbk2utf8(gbkfile: &str, utf8file: &str) -> FileResult<()> {
    let s = read_gbk_file_str(gbkfile)?;
    let mut file = check_result(File::create(utf8file))?;
    check_result(file.write_all(s.as_bytes()))
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 10:07:46
/// @Description: gbk编码文件转utf8文件
pub fn utf82gbk(utf8file: &str, gbk_file: &str) -> FileResult<()> {
    let s = read_utf8_file_str(utf8file)?;

    let v8 = check_result(GBK.encode(&s, EncoderTrap::Strict))?;

    let mut file = check_result(File::create(gbk_file))?;
    check_result(file.write_all(&v8))
}
/// @Author: DengLibin
/// @Date: Create in 2024-07-25 18:39:48
/// @Description: gbk编码的字符串
pub fn get_gbk_str(gbk_bytes: &Vec<u8>) -> FileResult<String> {
    let decoded_string = GBK.decode(gbk_bytes, DecoderTrap::Replace);
    check_result(decoded_string)
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 10:19:11
/// @Description: 复制文件 返回文件大小
pub fn copy_file(src_file: &str, dest_file: &str) -> FileResult<u64> {
    //函数用于以只读模式打开一个已经存在的文件，如果文件不存在，则会抛出一个错误。如果文件不可读，那么也会抛出一个错误
    let mut s_f: std::fs::File = check_result(std::fs::File::open(src_file))?;

    //创建文件，创建一个文件并返回创建的文件句柄。如果文件已经存在，则会内部调用 open() 打开文件。如果创建失败，比如目录不可写，则会抛出错误
    let mut w_f = check_result(File::create(dest_file))?;

    let mut size: usize = 0_usize;
    let mut buffer: [u8; 10240] = [0_u8; 10240]; //缓冲区
    loop {
        //读入缓冲区
        let len = check_result(s_f.read(&mut buffer))?;
        //写
        let x = check_result(w_f.write(&buffer[0..len]))?;
        let _ = check_result(w_f.flush())?;
        size += x;
        //读完了
        if len < buffer.len() {
            break;
        }
    }
    check_result(w_f.flush())?;
    Ok(size as u64)
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 10:32:19
/// @Description: 追加字符串
pub fn append_str(filepath: &str, str: &str) -> FileResult<()> {
    //文件不存在则创建
    let mut file = check_result(OpenOptions::new().create(true).append(true).open(filepath))?;
    check_result(file.write_all(str.as_bytes()))
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 10:32:19
/// @Description: 写入字符串，文件已存在会覆盖
pub fn write_str(filepath: &str, str: &str) -> FileResult<()> {
    //文件不存在则创建
    let mut file = check_result(OpenOptions::new().create(true).write(true).open(filepath))?;
    check_result(file.write_all(str.as_bytes()))
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 11:04:52
/// @Description: 是否存在
pub fn exist(file_path: &str) -> bool {
    let target_path = Path::new(file_path);
    return target_path.exists();
}
/// @Author: DengLibin
/// @Date: Create in 2023-11-02 11:38:57
/// @Description: 获取文件名
pub fn get_filename(file_path: &str) -> Option<String> {
    let path = Path::new(file_path);
    let o = path.file_name();
    if let Some(s) = o {
        if let Some(s) = s.to_str() {
            return Some(String::from(s));
        }
    }
    return None;
}

/// @Author: DengLibin
/// @Date: Create in 2023-11-02 11:16:25
/// @Description: 递归文件夹
pub fn recursion_dir(dir_path: &str) -> FileResult<()> {
    let entrys = check_result(fs::read_dir(dir_path))?;
    for entry in entrys {
        let entry = check_result(entry)?;
        let path = entry.path();
        let metadata = check_result(fs::metadata(&path))?;

        let o = path.as_os_str().to_str();
        if let Some(p) = o {
            if metadata.is_dir() {
                //文件夹
                recursion_dir(p)?;
            } else if metadata.is_file() {
                //文件
                println!("{}", p);
            }
        }
    }

    Ok(())
}
/// @Author: DengLibin
/// @Date: Create in 2024-04-21 22:21:22
/// @Description: 读取文件到缓冲区
pub fn read_file_to_buffer(filename: &str) -> FileResult<Vec<u8>> {
    let mut file = check_result(File::open(filename))?;
    let mut buffer = Vec::new();
    check_result(file.read_to_end(&mut buffer))?;
    Ok(buffer)
}

 /// @Author: DengLibin
 /// @Date: Create in 2024-08-02 15:59:38
 /// @Description: 删除非空文件夹
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
    if path.as_ref().is_dir() {
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                remove_dir_all(path)?;
            } else {
                fs::remove_file(path)?;
            }
        }
        fs::remove_dir(path)?;
    } else {
        fs::remove_file(path)?;
    }
    Ok(())
}

/// @Author: DengLibin
/// @Date: Create in 2024-06-12 11:57:16
/// @Description: 按行读取
/// @param func: 消费函数，返回true 继续读下一行，返回false 终止读
pub fn read_by_line<F: FnMut(String) -> bool>(path: &str, mut func: F) -> FileResult<()> {
    // 打开文件
    let file = check_result(File::open(path))?;

    // 创建BufReader
    let reader = BufReader::new(file);

    // 逐行读取文件
    for line in reader.lines() {
        // 如果读取行时没有错误，打印行内容
        match line {
            Ok(content) => {
                let r = func(content);
                if !r {
                    return Ok(());
                }
            }
            Err(err) => {
                return Result::Err(FileError {
                    msg: err.to_string(),
                });
            }
        }
    }
    Ok(())
}

use serde::{Deserialize, Serialize};



#[derive(Debug, Serialize, Deserialize)]
pub struct SyncFile {
    path: String,                         //文件路径
    children: Vec<Box<Option<SyncFile>>>, //子集
}

/// @Author: DengLibin
/// @Date: Create in 2024-12-24 16:13:56
/// @Description: 递归读取文件夹中的文件
pub fn read_dir(dir: &str) -> Result<Vec<SyncFile>, Box<dyn std::error::Error>> {
    let mut files_vec: Vec<SyncFile> = vec![];
    let files = fs::read_dir(dir)?;
    for file in files {
        let f = file?;
        let path = f.path();
        let path = path.to_str().unwrap();
        let meta_data = f.metadata()?;
        if meta_data.is_file() {
            let sync_file = SyncFile {
                path: path.replace(dir, ""),
                children: vec![],
            };
            files_vec.push(sync_file);
        } else if meta_data.is_dir() {
            let children = read_dir(path)?;
            let children = children.into_iter().map(|f| Box::new(Some(f))).collect();
            let sync_file = SyncFile {
                path: path.into(),
                children: children,
            };
            files_vec.push(sync_file);
        }
    }

    Ok(files_vec)
}
